package co.yixiang.yshop.module.pay.service.wechat;

import co.yixiang.yshop.module.pay.config.WechatPayProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
@RequiredArgsConstructor
public class WechatV3VerifierService {

    private final WechatPayProperties properties;
    private final ResourceLoader resourceLoader;

    private volatile X509Certificate platformCert;
    private volatile String platformCertSerialUpperHex;

    // 简易 nonce 去重缓存（进程内），生产可替换为 Redis
    private final Map<String, Long> nonceCache = new ConcurrentHashMap<>();

    public synchronized void initIfNecessary() {
        if (platformCert != null) return;
        try {
            String path = properties.getPlatformCertPath();
            if (path == null || path.isEmpty()) {
                log.warn("[WeChatV3] platformCertPath is empty, skip loading platform certificate");
                return;
            }
            Resource resource = resourceLoader.getResource(path);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            platformCert = (X509Certificate) cf.generateCertificate(resource.getInputStream());
            platformCertSerialUpperHex = platformCert.getSerialNumber().toString(16).toUpperCase();
            log.info("[WeChatV3] platform certificate loaded: subject={} serialNo={}",
                    platformCert.getSubjectX500Principal(), platformCertSerialUpperHex);
        } catch (Exception e) {
            throw new IllegalStateException("Load WeChat platform certificate failed", e);
        }
    }

    public boolean verifySignature(Map<String, String> headers, String body) {
        try {
            initIfNecessary();
            if (platformCert == null) {
                log.error("[WeChatV3] platform certificate not configured, reject in prod");
                return false;
            }
            String timestamp = getHeader(headers, "wechatpay-timestamp");
            String nonce = getHeader(headers, "wechatpay-nonce");
            String signature = getHeader(headers, "wechatpay-signature");
            String serial = getHeader(headers, "wechatpay-serial");

            // 时间窗口校验
            long ts = Long.parseLong(timestamp);
            long now = Instant.now().getEpochSecond();
            long skew = Math.abs(now - ts);
            if (skew > properties.getAllowedTimestampSkewSeconds()) {
                log.error("[WeChatV3] timestamp skew too large, now={}, ts={}, skew={}", now, ts, skew);
                return false;
            }

            // nonce 去重
            long ttl = properties.getNonceTtlSeconds();
            long expireAt = now + ttl;
            Long exists = nonceCache.putIfAbsent(nonce, expireAt);
            if (exists != null) {
                log.error("[WeChatV3] nonce replay detected: {}", nonce);
                return false;
            }
            // 清理过期
            nonceCache.entrySet().removeIf(e -> e.getValue() < now);

            // 证书序列号校验（大写 hex 对比）
            if (platformCertSerialUpperHex != null && !Objects.equals(platformCertSerialUpperHex, serial.toUpperCase())) {
                log.error("[WeChatV3] serial mismatch, expect={}, got={}", platformCertSerialUpperHex, serial);
                return false;
            }

            String message = timestamp + "\n" + nonce + "\n" + body + "\n";
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(platformCert.getPublicKey());
            sign.update(message.getBytes(StandardCharsets.UTF_8));
            boolean ok = sign.verify(Base64.getDecoder().decode(signature));
            if (!ok) log.error("[WeChatV3] signature verify failed");
            return ok;
        } catch (Exception e) {
            log.error("[WeChatV3] signature verify exception", e);
            return false;
        }
    }

    private String getHeader(Map<String, String> headers, String key) {
        String v = headers.get(key);
        if (v == null) v = headers.get(key.substring(0, 1).toUpperCase() + key.substring(1));
        if (v == null) v = headers.get(key.toUpperCase());
        return v == null ? "" : v;
    }

    public String decryptResource(String associatedData, String nonce, String ciphertextBase64) {
        try {
            String apiV3Key = properties.getApiV3Key();
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            if (associatedData != null) {
                cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            }
            byte[] cipherData = Base64.getDecoder().decode(ciphertextBase64);
            byte[] plain = cipher.doFinal(cipherData);
            return new String(plain, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new IllegalStateException("WeChat V3 decrypt failed", e);
        }
    }
} 